﻿using System.Collections.Generic;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.AutoMapper;
using MyCompanyName.AbpZeroTemplate.Authorization.Dto;
using MyCompanyName.AbpZeroTemplate.Authorization.Roles.Dto;
using MyCompanyName.AbpZeroTemplate.Prm.Authorization;
using System;
using Abp.Domain.Repositories;
using MyCompanyName.AbpZeroTemplate.Prm.System;
using System.Dynamic;

namespace MyCompanyName.AbpZeroTemplate.Authorization.Roles
{
    /// <summary>
    /// Application service that is used by 'role management' page.
    /// </summary>
    [AbpAuthorize(AppPermissions.Pages_Administration_Roles)]
    public class RoleAppService : AbpZeroTemplateAppServiceBase, IRoleAppService
    {
        private readonly RoleManager _roleManager;

        private readonly IRepository<SystemRoleFunctionActions, int> systemRoleFunctionActionsRepository;
        private readonly IRepository<SystemRoleDocmentActions, int> systemRoleDocmentActionsRepository;
        private readonly IRepository<DocumentType, int> documentTypeRepository;

        public RoleAppService(RoleManager roleManager,
            IRepository<SystemRoleFunctionActions, int> _systemRoleFunctionActionsRepository,
            IRepository<SystemRoleDocmentActions, int> _systemRoleDocmentActionsRepository,
            IRepository<DocumentType, int> _documentTypeRepository)
        {
            systemRoleFunctionActionsRepository = _systemRoleFunctionActionsRepository;
            systemRoleDocmentActionsRepository = _systemRoleDocmentActionsRepository;
            documentTypeRepository = _documentTypeRepository;
            _roleManager = roleManager;
        }

        public async Task<ListResultOutput<RoleListDto>> GetRoles()
        {
            var roles = await _roleManager.Roles.ToListAsync();
            return new ListResultOutput<RoleListDto>(roles.MapTo<List<RoleListDto>>());
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Roles_Create, AppPermissions.Pages_Administration_Roles_Edit)]
        public async Task<GetRoleForEditOutput> GetRoleForEdit(NullableIdInput input)
        {
            var permissions = PermissionManager.GetAllPermissions();
            var grantedPermissions = new Permission[0];
            RoleEditDto roleEditDto;

            if (input.Id.HasValue) //Editing existing role?
            {
                var role = await _roleManager.GetRoleByIdAsync(input.Id.Value);
                grantedPermissions = (await _roleManager.GetGrantedPermissionsAsync(role)).ToArray();
                roleEditDto = role.MapTo<RoleEditDto>();
            }
            else
            {
                roleEditDto = new RoleEditDto();
            }

            return new GetRoleForEditOutput
                   {
                       Role = roleEditDto,
                       Permissions = permissions.MapTo<List<FlatPermissionDto>>().OrderBy(p => p.DisplayName).ToList(),
                       GrantedPermissionNames = grantedPermissions.Select(p => p.Name).ToList()
                   };
        }

        public async Task CreateOrUpdateRole(CreateOrUpdateRoleInput input)
        {
            if (input.Role.Id.HasValue)
            {
                await UpdateRoleAsync(input);
            }
            else
            {
                await CreateRoleAsync(input);
            }
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Roles_Delete)]
        public async Task DeleteRole(EntityRequestInput input)
        {
            var role = await _roleManager.GetRoleByIdAsync(input.Id);
            CheckErrors(await _roleManager.DeleteAsync(role));
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Roles_Edit)]
        protected virtual async Task UpdateRoleAsync(CreateOrUpdateRoleInput input)
        {
            Debug.Assert(input.Role.Id != null, "input.Role.Id should be set.");

            var role = await _roleManager.GetRoleByIdAsync(input.Role.Id.Value);
            role.DisplayName = input.Role.DisplayName;
            role.IsDefault = input.Role.IsDefault;

            await UpdateGrantedPermissionsAsync(role, input.GrantedPermissionNames);
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Roles_Create)]
        protected virtual async Task CreateRoleAsync(CreateOrUpdateRoleInput input)
        {
            var role = new Role(AbpSession.TenantId, input.Role.DisplayName) { IsDefault = input.Role.IsDefault };
            CheckErrors(await _roleManager.CreateAsync(role));
            await CurrentUnitOfWork.SaveChangesAsync(); //It's done to get Id of the role.
            await UpdateGrantedPermissionsAsync(role, input.GrantedPermissionNames);
        }

        private async Task UpdateGrantedPermissionsAsync(Role role, List<string> grantedPermissionNames)
        {
            var grantedPermissions = PermissionManager.GetPermissionsFromNamesByValidating(grantedPermissionNames);
            await _roleManager.SetGrantedPermissionsAsync(role, grantedPermissions);
        }


        public List<GetProjectPermissionOutPut> GetProjectPermission(int roleId)
        {
            List<GetProjectPermissionOutPut> result = new List<GetProjectPermissionOutPut>();

            foreach (var item in Enum.GetValues(typeof(ProjectFunctions)))
            {
                var fun = systemRoleFunctionActionsRepository.GetAll().Where(x => x.SystemRoleId == roleId && x.Permission == (ProjectFunctions)item).SingleOrDefault();
                var outPut = new GetProjectPermissionOutPut()
                {
                    Permission = item.ToString(),
                    HasAuth = false,
                    Scope = "null"
                };
                if (fun != null)
                {
                    outPut.HasAuth = true;
                    outPut.Scope = fun.ProjectScope.ToString();
                }
                result.Add(outPut);
            }
            return result;
        }


        public async Task SetProjectPermission(SetProjectPermissionInPut input)
        {
            await systemRoleFunctionActionsRepository.DeleteAsync(x => x.SystemRoleId == input.RoleId);
            foreach (var item in input.Permissions)
            {
                if (item.HasAuth)
                {
                    await systemRoleFunctionActionsRepository.InsertAsync(new SystemRoleFunctionActions()
                    {
                        SystemRoleId = input.RoleId,
                        Permission = (ProjectFunctions)Enum.Parse(typeof(ProjectFunctions), item.Permission),
                        ProjectScope = (ActionScope)Enum.Parse(typeof(ActionScope), item.Scope)
                    });
                }
            }
        }



        public async Task<List<dynamic>> GetDocumentPermission(int roleId)
        {
            List<dynamic> result = new List<dynamic>();

            var types = await documentTypeRepository.GetAll().ToListAsync();

            foreach (var item in Enum.GetValues(typeof(DocmentFunctions)))
            {
                dynamic dy = new ExpandoObject();
                dy.DocumentAuth = item.ToString();
                foreach (var type in types)
                {
                    ((IDictionary<string, object>)dy).Add(type.Name, HasDocumentAction(roleId, type.Id, (DocmentFunctions)item));
                }
                result.Add(dy);
            }
            return result;
        }



        public bool HasDocumentAction(int roleId, int typeId, DocmentFunctions action)
        {
            var hasAuth = systemRoleDocmentActionsRepository.GetAll().Where(x => x.SystemRoleId == roleId && x.DocTypeId == typeId && x.Permission == action).Any();
            return hasAuth;
        }







        public async Task SetDocumentPermission(SetDocumentPermissionInPut input)
        {
            systemRoleDocmentActionsRepository.Delete(x => x.SystemRoleId == input.RoleId);
            var types = await documentTypeRepository.GetAll().ToListAsync();
            foreach (var item in input.records)
            {
                var permission = (DocmentFunctions)Enum.Parse(typeof(DocmentFunctions), item["documentAuth"].Value);
                foreach (var r in types)
                {
                    var t = item[r.Name.ToLower()];
                    if (t != null && t.Value)
                    {
                        systemRoleDocmentActionsRepository.Insert(new SystemRoleDocmentActions()
                        {
                            Permission = permission,
                            SystemRoleId = input.RoleId,
                            DocTypeId = r.Id
                        });
                    }
                }
            }
        }
    }
}
